use chrono::{NaiveDate, Utc};
use sea_orm::{DatabaseBackend, MockDatabase, MockExecResult};
use crate::grpc::reservation::ReservationStatus;

use crate::entity::reservation::Model as ReservationModel;
use crate::service::ReservationService;

#[tokio::test]
async fn test_create_reservation() {
    let db = MockDatabase::new(DatabaseBackend::Sqlite)
        .append_exec_results(vec![MockExecResult {
            last_insert_id: 1,
            rows_affected: 1,
        }])
        .append_query_results(vec![vec![ReservationModel {
            id: 1,
            guest_name: "John Doe".to_string(),
            guest_email: "john@example.com".to_string(),
            guest_phone: "1234567890".to_string(),
            room_number: "101".to_string(),
            check_in_date: NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
            check_out_date: NaiveDate::from_ymd_opt(2024, 1, 3).unwrap(),
            status: "PENDING".to_string(),
            total_amount: 200.0,
            created_at: Utc::now().naive_utc(),
            updated_at: Utc::now().naive_utc(),
        }]])
        .into_connection();

    let service = ReservationService::new(db);

    let result = service
        .create_reservation(
            "John Doe".to_string(),
            "john@example.com".to_string(),
            "1234567890".to_string(),
            "101".to_string(),
            NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
            NaiveDate::from_ymd_opt(2024, 1, 3).unwrap(),
            200.0,
        )
        .await;

    assert!(result.is_ok());
    let reservation = result.unwrap();
    assert_eq!(reservation.guest_name, "John Doe");
    assert_eq!(reservation.status, "PENDING");
}

#[tokio::test]
async fn test_update_reservation_status() {
    let db = MockDatabase::new(DatabaseBackend::Sqlite)
        .append_query_results(vec![vec![ReservationModel {
            id: 1,
            guest_name: "John Doe".to_string(),
            guest_email: "john@example.com".to_string(),
            guest_phone: "1234567890".to_string(),
            room_number: "101".to_string(),
            check_in_date: NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
            check_out_date: NaiveDate::from_ymd_opt(2024, 1, 3).unwrap(),
            status: "PENDING".to_string(),
            total_amount: 200.0,
            created_at: Utc::now().naive_utc(),
            updated_at: Utc::now().naive_utc(),
        }]])
        .append_exec_results(vec![MockExecResult {
            last_insert_id: 1,
            rows_affected: 1,
        }])
        .into_connection();

    let service = ReservationService::new(db);

    let result = service
        .update_reservation_status(1, ReservationStatus::Paid)
        .await;

    assert!(result.is_ok());
    assert!(result.unwrap().is_some());
}

#[tokio::test]
async fn test_list_reservations() {
    let db = MockDatabase::new(DatabaseBackend::Sqlite)
        .append_query_results(vec![vec![
            ReservationModel {
                id: 1,
                guest_name: "John Doe".to_string(),
                guest_email: "john@example.com".to_string(),
                guest_phone: "1234567890".to_string(),
                room_number: "101".to_string(),
                check_in_date: NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
                check_out_date: NaiveDate::from_ymd_opt(2024, 1, 3).unwrap(),
                status: "PENDING".to_string(),
                total_amount: 200.0,
                created_at: Utc::now().naive_utc(),
                updated_at: Utc::now().naive_utc(),
            },
            ReservationModel {
                id: 2,
                guest_name: "Jane Smith".to_string(),
                guest_email: "jane@example.com".to_string(),
                guest_phone: "0987654321".to_string(),
                room_number: "102".to_string(),
                check_in_date: NaiveDate::from_ymd_opt(2024, 1, 5).unwrap(),
                check_out_date: NaiveDate::from_ymd_opt(2024, 1, 7).unwrap(),
                status: "PAID".to_string(),
                total_amount: 300.0,
                created_at: Utc::now().naive_utc(),
                updated_at: Utc::now().naive_utc(),
            },
        ]])
        .into_connection();

    let service = ReservationService::new(db);

    let result = service
        .list_reservations(1, 10, None, None, None)
        .await;

    assert!(result.is_ok());
    let (reservations, total) = result.unwrap();
    assert_eq!(reservations.len(), 2);
    assert_eq!(total, 2);
}

#[tokio::test]
async fn test_model_to_grpc_conversion() {
    let model = ReservationModel {
        id: 1,
        guest_name: "Test User".to_string(),
        guest_email: "test@example.com".to_string(),
        guest_phone: "1234567890".to_string(),
        room_number: "101".to_string(),
        check_in_date: NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
        check_out_date: NaiveDate::from_ymd_opt(2024, 1, 3).unwrap(),
        status: "PAID".to_string(),
        total_amount: 200.0,
        created_at: Utc::now().naive_utc(),
        updated_at: Utc::now().naive_utc(),
    };

    let grpc = super::super::service::ReservationService::model_to_grpc(model);
    
    assert_eq!(grpc.id, "1");
    assert_eq!(grpc.guest_name, "Test User");
    assert_eq!(grpc.status, ReservationStatus::Paid as i32);
}